package ru.osu.acm.testing;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.faces.context.FacesContext;
import javax.faces.component.UIViewRoot;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.HashMap;
import java.io.*;
import java.sql.Connection;
import java.sql.SQLException;

import ru.osu.acm.system.Logger;

/**
 * User: Aleksey
 * Date: 24.02.2009
 * Time: 1:31:02
 */
public class Utils {
    private static class Item {
        int start;
        int end;

        private Item(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }

    private static ArrayList<Item> strings = new ArrayList<Item>();
    private static ArrayList<Item> comments = new ArrayList<Item>();

    private static void getStringsAndComments(String source) {
        strings.clear();
        comments.clear();
        boolean isString = false;
        boolean isComment = false;
        boolean commentLine = false;

        int startComment = -1;
        int startString = -1;

        for (int i = 0; i < source.length(); i++) {
            if (!isString && !isComment) {
                if (source.charAt(i) == '"') {
                    isString = true;
                    startString = i;
                }
                else if (i > 0 && source.charAt(i) == '/' && source.charAt(i-1) == '/') {
                    isComment = true;
                    commentLine = true;
                    startComment = i-1;
                }
                else if (i > 0 && source.charAt(i) == '*' && source.charAt(i-1) == '/') {
                    isComment = true;
                    commentLine = false;
                    startComment = i-1;
                }
            }
            else if (isString) {
                if (source.charAt(i) == '"') {
                    int x = i-1;
                    int cnt = 0;
                    while (x >= 0 && source.charAt(x) == '\\') {
                        cnt++;
                        x--;
                    }
                    if (cnt % 2 == 0) {
                        isString = false;
                        strings.add(new Item(startString, i+1));
                    }
                }
            }
            else if (isComment) {
                if (commentLine && source.charAt(i) == '\n') {
                    isComment = false;
                    comments.add(new Item(startComment, i));
                }
                else if (!commentLine && source.charAt(i) == '/' && source.charAt(i-1) == '*') {
                    isComment = false;
                    comments.add(new Item(startComment, i+1));
                }
            }
        }
    }

    private static boolean isInStringOrComment(int index) {
        for (Item string : strings) {
            if (index >= string.start && index < string.end) {
                return true;
            }
        }
        for (Item comment : comments) {
            if (index >= comment.start && index < comment.end) {
                return true;
            }
        }
        return false;
    }

    private static boolean isInComment(int index) {
        for (Item comment : comments) {
            if (index >= comment.start && index < comment.end) {
                return true;
            }
        }
        return false;
    }

    private static boolean isIdentifierChar(char c) {
        return Character.isDigit(c) || Character.isLetter(c) || c == '_';
    }

    public static String getJavaClassName(String source) {
        getStringsAndComments(source);

        String className = "";

        int lastClassIndex = -1;
        while (true) {
            int classIndex = source.indexOf("class", lastClassIndex+1);
            lastClassIndex = classIndex;

            if (classIndex == -1) {
                break;
            }
            if (isInStringOrComment(classIndex)) {
                continue;
            }
            if (classIndex == 0) {
                continue;
            }
            if (classIndex + "class".length() >= source.length()) {
                continue;
            }
            if (isIdentifierChar(source.charAt(classIndex - 1)) || isIdentifierChar(source.charAt(classIndex + "class".length()))) {
                continue;
            }

            Boolean isPublic = false;
            int x = classIndex - 1;
            for (int i = 0; i < 3; i++) {
                while (x >= 0 && (isInComment(x) || Character.isWhitespace(source.charAt(x)))) {
                    x--;
                }
                String modifier = "";
                while (x >= 0 && isIdentifierChar(source.charAt(x))) {
                    modifier = source.charAt(x) + modifier;
                    x--;
                }
                if (modifier.equals("public")) {
                    isPublic = true;
                    break;
                }
                if (!modifier.equals("abstract") && !modifier.equals("final") && !modifier.equals("strictfp")) {
                    break;
                }
            }
            if (isPublic) {
                x = classIndex + "class".length();
                while (x < source.length() && (isInComment(x) || Character.isWhitespace(source.charAt(x)))) {
                    x++;
                }
                className = "";
                while (x < source.length() && isIdentifierChar(source.charAt(x))) {
                    className = className + source.charAt(x);
                    x++;
                }
                break;
            }
        }

        return (className.equals("") ? "Solution" : className);
    }

    public static String getJavaClassName(File file) {
        String source = "";
        try {
            BufferedReader br = new BufferedReader(new FileReader(file));
            String line = br.readLine();
            while (line != null) {
                source += (line + '\n');
                line = br.readLine();
            }
            br.close();
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return getJavaClassName(source);
    }

    public static String getRequestParam(String param) {
        String p = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(param);
        if (p == null) {
            return "";
        }
        return p.trim();
    }

    public static String getLocaleMessage(String param){
        FacesContext context= FacesContext.getCurrentInstance();
        UIViewRoot viewRoot = context.getViewRoot();
        Locale locale = viewRoot.getLocale();

        ClassLoader loader= Thread.currentThread().getContextClassLoader();

        ResourceBundle bundle = ResourceBundle.getBundle("messages", locale, loader);

        return bundle.getString(param);
    }

    public static Connection getConnection(DataSource source) {
        Connection con = null;
        try {
            con = source.getConnection();
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    public static void clearDir(File dir) {
        try {
            File[] children = dir.listFiles();
            for (File child : children) {
                if (child.isDirectory()) {
                    clearDir(child);
                }
                child.delete();
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static ArrayList<HashMap<String, String>> getList(String path, File file) {
        ArrayList<HashMap<String, String>> res = new ArrayList<HashMap<String, String>>();
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(file);

            String[] parts = path.split("\\.");

            Node node = document.getElementsByTagName(parts[0]).item(0);
            for (int i = 1; i < parts.length - 1; i++) {
                NodeList list = node.getChildNodes();
                for (int j = 0; j < list.getLength(); j++) {
                    if (list.item(j).getNodeName().compareTo(parts[i]) == 0) {
                        node = list.item(j);
                        break;
                    }
                }
            }

            NodeList list = node.getChildNodes();
            for (int i = 0; i < list.getLength(); i++) {
                if (list.item(i).getNodeName().compareTo(parts[parts.length - 1]) == 0) {
                    HashMap<String, String> hm = new HashMap<String, String>();
                    NodeList list2 = list.item(i).getChildNodes();

                    for (int j = 0; j < list2.getLength(); j++) {
                        hm.put(list2.item(j).getNodeName(), list2.item(j).getTextContent());
                    }

                    int attrCnt = list.item(i).getAttributes().getLength();
                    for (int j = 0; j < attrCnt; j++) {
                        hm.put(list.item(i).getAttributes().item(j).getNodeName(), list.item(i).getAttributes().item(j).getNodeValue());
                    }

                    res.add(hm);
                }
            }
        }
        catch (IOException e) {
            Logger.logError(e);
        }
        catch (SAXException e) {
            Logger.logError(e);
        }
        catch (ParserConfigurationException e) {
            Logger.logError(e);
        }
        return res;
    }

    public static void copyFile(File in, File out) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(in);
            fos = new FileOutputStream(out);
            byte[] buf = new byte[1024];
            int i;
            while ((i = fis.read(buf)) != -1) {
                fos.write(buf, 0, i);
            }
        }
        catch (IOException e) {
            Logger.logError(e);
        }
        finally {
            if (fis != null) {
                try {
                    fis.close();
                }
                catch (IOException e) {
                    Logger.logError(e);
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                }
                catch (IOException e) {
                    Logger.logError(e);
                }
            }
        }
    }

    public static String getFileContentsTrimmed(File file) {
        String contents = "";
        try {
            BufferedReader br = new BufferedReader(new FileReader(file));
            String line = br.readLine();
            while (line != null) {
                contents += (line + '\n');
                line = br.readLine();
            }
            contents = contents.trim();
            br.close();
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return contents;
    }

    public static String prepareStringForHtml(String s) {
        s = s.replace("&", "&amp;");
        s = s.replace(">", "&gt;");
        s = s.replace("<", "&lt;");
        s = s.replace("\n", "<br/>");
        s = s.replace("\t", "    ");
        //s = s.replace(" ", "&nbsp;");
        return s;
    }

    public static String prepareSourceForHtml(String s) {
        s = s.replace("&", "&amp;");
        s = s.replace(">", "&gt;");
        s = s.replace("<", "&lt;");
        s = s.replace("\n", "<br/>");
        s = s.replace("\t", "    ");
        s = s.replace(" ", "&nbsp;");
        return s;
    }

    public static String getProblemLetter(int problemIndex) {
        return (char)('A' + problemIndex) + "";
    }
}
